#pragma once

#include <string>
#include "../models/medicinemodel.hpp"
#include "../views/medicineview.hpp"
#include "../util.hpp"

namespace pharmacy_server
{

    class MedicineController
    {
    public:
        static MedicineController& instance()
        {
            static MedicineController inst;
            return inst;
        }

    private:
        MedicineController()
            : medicinemodel(MedicineModel::instance())
        {}

        MedicineController(const MedicineController&) = delete;
        MedicineController& operator=(const MedicineController&) = delete;

    public:
        Result add_medicine(const std::string& name, const std::string& category, const std::string& supplier, float price, int stock, int threshold, int validity)
        {
            try
            {
                medicinemodel.add_medicine(name, category, supplier, price, stock, threshold, validity);
                return medicineview.add_medicine(true, "add medicine success");
            }
            catch (const model_except& e)
            {
                return medicineview.add_medicine(false, e.what());
            }
        }

        Result edit_medicine(int id, const std::string& name, const std::string& category, const std::string& supplier, float price, int stock, int threshold, int validity)
        {
            try
            {
                medicinemodel.update_medicine(id, name, category, supplier, price, stock, threshold, validity);
                return medicineview.edit_medicine(true, "edit medicine success");
            }
            catch (const model_except& e)
            {
                return medicineview.edit_medicine(false, e.what());
            }
        }

        Result delete_medicine(const std::string& name)
        {
            try
            {
                medicinemodel.delete_medicine(name);
                return medicineview.delete_medicine(true, "delete medicine success");
            }
            catch (const model_except& e)
            {
                return medicineview.delete_medicine(false, e.what());
            }
        }

        Result query_medicines_by_category(const std::string& category)
        {
            try
            {
                auto medicines = medicinemodel.query_medicines_by_category(category);

                Json::Value root;
                for (const auto* medicine : medicines)
                    root.append((*medicine).to_json());

                easylog::debug("{}", json_util::serialize(root));

                return medicineview.query_medicines_by_category(true, "query medicines by category success", json_util::serialize(root));
            }
            catch (const model_except& e)
            {
                return medicineview.query_medicines_by_category(false, e.what());
            }
        }

        Result query_medicines_by_keyword(const std::string& keyword)
        {
            try
            {
                auto medicines = medicinemodel.query_medicines_by_keyword(keyword);

                Json::Value root;
                for (const auto* medicine : medicines)
                    root.append((*medicine).to_json());

                return medicineview.query_medicines_by_keyword(true, "query medicines by keyword success", json_util::serialize(root));
            }
            catch (const model_except& e)
            {
                return medicineview.query_medicines_by_keyword(false, e.what());
            }
        }

        Result query_medicine_by_name(const std::string& name)
        {
            try
            {
                auto medicine = medicinemodel.query_medicine_by_name(name);
                return medicineview.query_medicine_by_name(true, "query medicine by name success", json_util::serialize(medicine->to_json()));
            }
            catch (const model_except& e)
            {
                return medicineview.query_medicine_by_name(false, e.what());
            }
        }

        Result get_medicine(int id)
        {
            try
            {
                auto medicine = medicinemodel.get_medicine(id);
                return medicineview.get_medicine(true, "query medicine by id success", json_util::serialize(medicine->to_json()));
            }
            catch (const model_except& e)
            {
                return medicineview.get_medicine(false, e.what());
            }
        }

        Result take_medicine(const std::string& username, const std::string& medicine_name, int quantity)
        {
            try
            {
                medicinemodel.take_medicine(username, medicine_name, quantity);
                return medicineview.take_medicine(true, "take medicine success");
            }
            catch (const model_except& e)
            {
                return medicineview.take_medicine(false, e.what());
            }
        }

    private:
        MedicineModel& medicinemodel;
        MedicineView medicineview;
    };
}